/* Copyright (c) 2009-2011, Kundan Singh. See LICENSING for details. */
package my.core.video.record
{
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.NetStatusEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.net.ObjectEncoding;
	import flash.net.SharedObject;
	
	import mx.events.PropertyChangeEvent;
	
	import my.core.User;
	import my.core.Util;

	/**
	 * Dispatched when the recording begins.
	 */
	[Event(name="open", type="flash.events.Event")]
	
	/**
	 * Dispatched when the recording ends, e.g., due to network connection close with the server.
	 */
	[Event(name="close", type="flash.events.ErrorEvent")]

	/**
	 * The Record object represents the data model for the video recording. It stores the state, stream
	 * for the recording.
	 */
	public class Record extends EventDispatcher
	{
		//--------------------------------------
		// PRIVATE VARIABLES
		//--------------------------------------
		
		private var _url:String;
		private var nc:NetConnection;
		private var ns:NetStream;
		private var _id:String;
		private var _user:User;
		private var _lastError:String;
		private var _recording:Boolean = true;
		
		/*
		 * Static constructor for setting the default encoding to AMF0.
		 */
		{
			// our server supports only AMF0 for now.
			NetConnection.defaultObjectEncoding = ObjectEncoding.AMF0;
			SharedObject.defaultObjectEncoding = ObjectEncoding.AMF0;
		}
		
		/**
		 * Construct a new record object. A new random stream id is also assigned.
		 */
		public function Record()
		{
			_id = Util.createId();
		}
		
		//--------------------------------------
		// GETTERS/SETTERS
		//--------------------------------------
		
		/**
		 * The url property represents the RTMP URL for recording. Together with the url and id property
		 * the recording can be played back using RTMP player.
		 */
		public function get url():String
		{
			return _url;
		}
		
		/**
		 * The id property represents the stream id. It is randomly generated by the constructor.
		 */
		public function get id():String
		{
			return _id;
		}
		
		/**
		 * Any last error based on the events from NetConnection or NetStream.
		 */
		public function get lastError():String
		{
			return _lastError;
		}
		
		/**
		 * The user property supplies the devices (camera and microphone) for recording and the server part
		 * of the url for NetConnection creation to the server. For a user with email "kundan10@gmail.com"
		 * the url is set as "rtmp://server:port/record/kundan10@gmail.com". Note that if the user is not
		 * logged in and does not have a email set, then email of "null" is used. Thus all guest users 
		 * do recording under URL "rtmp://server:port/record/null"
		 */ 
		public function get user():User
		{
			return _user;
		}
		public function set user(value:User):void
		{
			var oldValue:User = _user;
			_user = value;
			if (oldValue != value) {
				if (oldValue != null) {
					oldValue.removeEventListener("cameraChange", deviceChangeHandler);
					oldValue.removeEventListener("micChange", deviceChangeHandler);
					oldValue.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChangeHandler);
				}
				if (value != null) {
					value.addEventListener("cameraChange", deviceChangeHandler, false, 0, true);
					value.addEventListener("micChange", deviceChangeHandler, false, 0, true);
					value.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, propertyChangeHandler, false, 0, true);
					
					_url = 'rtmp://' + value.serverFlash + '/record/' + value.email;
				}
				else {
					_url = null;
				}
			}
		}
		
		/**
		 * Whether we are currently recording or not? When set, it attaches the network stream with the
		 * devices and starts streaming recording to the server.
		 */
		public function get recording():Boolean
		{
			return _recording;
		}
		public function set recording(value:Boolean):void
		{
			_recording = value;
			if (ns != null && user != null) {
				if (value) {
					if (user.camActive && user.camera != null)
						ns.attachCamera(user.camera);
					if (user.micActive && user.mic != null)
						ns.attachAudio(user.mic);
				}
				else {
					ns.attachCamera(null);
					ns.attachAudio(null);
				}
			}
		}
		
		//--------------------------------------
		// PUBLIC METHODS
		//--------------------------------------
		
		/**
		 * This method initiates a connection to the RTMP server for recording using the previously set 
		 * attributes such as url derived from the user object.
		 */
		public function open():void
		{
			if (nc == null) {
				_lastError = null;
				nc = new NetConnection();
				nc.addEventListener(NetStatusEvent.NET_STATUS, recordNetStatusHandler, false, 0, true);
				nc.addEventListener(IOErrorEvent.IO_ERROR, recordErrorHandler, false, 0, true);
				nc.addEventListener(SecurityErrorEvent.SECURITY_ERROR, recordErrorHandler, false, 0, true);
				nc.connect(_url);
			}
		}
		
		/**
		 * This method disconnects the connection with the server.
		 */
		public function close():void
		{
			if (nc != null) {
				nc.close();
				nc = null;
			}
			if (ns != null) {
				ns.close();
				ns = null;
			}
		}
		
		//--------------------------------------
		// PRIVATE METHODS
		//--------------------------------------
		
		// process the events on NetConnection.
		
		private function recordNetStatusHandler(event:NetStatusEvent):void
		{
			trace("recordNetStatusHandler " + event.info.code);
			switch (event.info.code) {
				case "NetConnection.Connect.Success":
					ns = new NetStream(nc);
					ns.addEventListener(NetStatusEvent.NET_STATUS, recordNetStatusHandler, false, 0, true);
					ns.publish(_id, "record");
					if (user != null) {
						if (user.camActive && user.camera != null)
							ns.attachCamera(user.camera);
						if (user.micActive && user.mic != null)
							ns.attachAudio(user.mic);
					}
					break;
				case "NetConnection.Connect.Failed":
				case "NetConnection.Connect.Rejected":
					_lastError = event.info.code + ': ' + event.info.description;
					/* fall through */
				case "NetConnection.Connect.Closed":
					close();
					dispatchEvent(new ErrorEvent("close", false, false, event.info.code));
					break;
			}
		}
		
		// on error, close this data model.
		private function recordErrorHandler(event:ErrorEvent):void
		{
			_lastError = event.toString();
			close();
			dispatchEvent(new ErrorEvent("close", false, false, event.toString()));
		}

		// when user's devices change, update our device attachment with the stream.
		private function deviceChangeHandler(event:Event):void
		{
			if (ns != null) {
				ns.attachCamera(user.camActive ? user.camera : null);
				ns.attachAudio(user.micActive ? user.mic : null);
			}
		}
		
		// when the user's camera and microphone status changes, update our device attachment.
		private function propertyChangeHandler(event:PropertyChangeEvent):void
		{
			var p:String = event.property as String;
			if (ns != null) {
				if (p == "camActive")
					ns.attachCamera(user.camActive ? user.camera : null);
				if (p == "micActive") 
					ns.attachAudio(user.micActive ? user.mic : null);
			}
		}
	}
}